P4457 [BJOI2018]治疗之雨

闲扯

据说这是一个套路题,果然我还是太嫩了呀。

题面

P4457 [BJOI2018]治疗之雨

Solution

我们设 $E(i)$ 表示当前剩余生命值为 $i$ 时,英雄死亡的剩余步数,其中 $E(0)=0$ 。

我们将每一个 $E(i)$ 看做一个点,将它们之间的转移看做一条有向边,那么我们就可以得到一个图。

我们设 $f_i$ 表示每一轮中,英雄减少 $i$ 点生命值的概率。

我们有:

表示我们选了 $i$ 个来攻击英雄,剩余的随机攻击所有的随从。

设 $g_{i,j}$ 表示当前生命值为 $i$ ,生命值变成 $j$ 的概率。

  • 对 $\forall i\in[1,n),j\in[1,i]$ ,我们有 $g_{i,j}=\frac{m}{m+1}f_{i-j}+\frac{1}{m+1}f_{i-j+1}$ 。
  • 对 $\forall i\in[1,n),j=i+1$ ,我们有 $g_{i,j}=\frac{1}{m+1}f_{0}$ 。
  • $g_{n,i}=f_{n-i}$ 。

那么,我们可以得到如下转移方程:

这显然不是一个 $DAG$ ,但是,我们可以发现,恰有有 $n$ 个未知数, $n$ 个方程,可以通过高斯消元来求解。

复杂度是 $O(n^3)$ 的,没法通过全部测试点。

但是可以发现这个矩阵很有特点,可以做到 $O(n^2)$ 求解。具体怎么做我也不知道

还有另外一种方法。

我们可以发现,第 $i$ 个方程,我们可以写成如下形式:

这就变成了由 $1\sim i$ 求出 $i+1$ 的形式了。

但是我们发现因为不知道 $E(1)$ ,所以我们没法推出剩余的值。

我们不妨设 $E(1)=x$ 。

那么我们可以推出 $E(i)=k_i\cdot x+b_i$ 。

由前 $n-1$ 个方程,我们可以推出 $E(n)=k_n\cdot x+b_n$ 。

而由第 $n$ 个方程,我们可以推出 $E(n)=k\cdot x+b$ 。

联立两个方程,求出 $x$ ,即可求出 $E(p)$ 。

特别的,如果 $k=0$ 或者 $k=1,n>1$ ,那么一定是打不死的,直接输出 $-1$ 即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include<bits/stdc++.h>
#define del(a,i) memset(a,i,sizeof(a))
#define ll long long
#define inl inline
#define il inl void
#define it inl int
#define ill inl ll
#define re register
#define ri re int
#define rl re ll
#define mid ((l+r)>>1)
#define lowbit(x) (x&(-x))
#define INF 0x3f3f3f3f
using namespace std;
template<class T>il read(T &x){
int f=1;char k=getchar();x=0;
for(;k>'9'||k<'0';k=getchar()) if(k=='-') f=-1;
for(;k>='0'&&k<='9';k=getchar()) x=(x<<3)+(x<<1)+k-'0';
x*=f;
}
template<class T>il _print(T x){
if(x/10) _print(x/10);
putchar(x%10+'0');
}
template<class T>il print(T x){
if(x<0) putchar('-'),x=-x;
_print(x);
}
ll mul(ll a,ll b,ll mod){long double c=1.;return (a*b-(ll)(c*a*b/mod)*mod)%mod;}
it qpow(int x,int m,int mod){
int res=1,bas=x;
while(m){
if(m&1) res=(1ll*res*bas)%mod;
bas=(1ll*bas*bas)%mod,m>>=1;
}
return res;
}
const int MAXN = 15e2+5,mod = 1e9+7;
int T,n,p,m,k,inv[MAXN],f[MAXN],g[MAXN][MAXN];
it add(int x,int y){
return x+y>=mod?x+y-mod:x+y;
}
it mul(int x,int y){
return 1ll*x*y%mod;
}
int k1[MAXN],b[MAXN];
il Solve(){
read(n),read(p),read(m),read(k);
if(k==0){
puts("-1");
return ;
}
if(m==0){
if(k==1&&n>1)
puts("-1");
else{
ri res=0;
while(p>0){
if(p<n)
++p;
p-=k,++res;
}
print(res),puts("");
}
return ;
}
int bas=qpow(m+1,mod-2,mod),im=qpow(m,mod-2,mod);
f[0]=mul(qpow(m,k,mod),qpow(bas,k,mod));
for(ri i=1;i<=n;++i){
if(i>k)
f[i]=0;
else
f[i]=mul(mul(mul(im,inv[i]),k-i+1),f[i-1]);
}
for(ri i=1;i<n;++i){
for(ri j=1;j<=i;++j)
g[i][j]=mul(bas,add(mul(m,f[i-j]),f[i-j+1]));
g[i][i+1]=mul(f[0],bas);
}
for(ri i=1;i<=n;++i)
g[n][i]=f[n-i];
k1[1]=1,b[0]=0;
for(ri i=1;i<n;++i){
k1[i+1]=b[i+1]=0;
for(ri j=1;j<i;++j)
k1[i+1]=add(k1[i+1],mod-mul(g[i][j],k1[j]));
k1[i+1]=add(k1[i+1],mod-mul(add(g[i][i],mod-1),k1[i]));
for(ri j=1;j<i;++j)
b[i+1]=add(b[i+1],mod-mul(g[i][j],b[j]));
b[i+1]=add(b[i+1],mod-mul(add(g[i][i],mod-1),b[i]));
b[i+1]=add(b[i+1],mod-1);
ri tmp=qpow(g[i][i+1],mod-2,mod);
k1[i+1]=mul(k1[i+1],tmp);
b[i+1]=mul(b[i+1],tmp);
}
int kk=0,bb=1;
for(ri i=1;i<n;++i)
kk=add(kk,mul(g[n][i],k1[i]));
for(ri i=1;i<n;++i)
bb=add(bb,mul(g[n][i],b[i]));
ri tmp=qpow(add(1,mod-g[n][n]),mod-2,mod);
kk=mul(kk,tmp),bb=mul(bb,tmp);
kk=add(k1[n],mod-kk);
bb=add(bb,mod-b[n]);
ri f1=mul(bb,qpow(kk,mod-2,mod));
ri ans=add(mul(k1[p],f1),b[p]);
print(ans),puts("");
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(T);
inv[1]=1;
for(ri i=2;i<=1500;++i)
inv[i]=mul(mod-mod/i,inv[mod%i]);
for(ri i=1;i<=T;++i)
Solve();
return 0;
}